<!DOCTYPE HTML>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Arxiv Sanity Preserver</title>

<!-- MathJax -->
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}});
</script>
<script type="text/javascript" async
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML">
</script>

<!-- CSS -->
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">

<!-- JS -->
<script src="{{ url_for('static', filename='jquery-1.8.3.min.js') }}"></script>
<script src="{{ url_for('static', filename='d3.min.js') }}"></script>
<script>

// passed in from flask as json
var papers = {{ papers | tojson }};
var msg = "{{ msg }}";
var render_format = "{{ render_format }}";
var username = "{{ g.user.username }}";

var urlq = '';

// helper function so that we can access keys in url bar
var QueryString = function () {
  // This function is anonymous, is executed immediately and 
  // the return value is assigned to QueryString!
  var query_string = {};
  var query = window.location.search.substring(1);
  var vars = query.split("&");
  for (var i=0;i<vars.length;i++) {
    var pair = vars[i].split("=");
        // If first entry with this name
    if (typeof query_string[pair[0]] === "undefined") {
      query_string[pair[0]] = decodeURIComponent(pair[1]);
        // If second entry with this name
    } else if (typeof query_string[pair[0]] === "string") {
      var arr = [ query_string[pair[0]],decodeURIComponent(pair[1]) ];
      query_string[pair[0]] = arr;
        // If third or later entry with this name
    } else {
      query_string[pair[0]].push(decodeURIComponent(pair[1]));
    }
  }
    return query_string;
}();


function jq( myid ) { return myid.replace( /(:|\.|\[|\]|,)/g, "\\$1" ); } // for dealing with ids that have . in them

function build_ocoins_str(p) {
  var ocoins_info = {
    "ctx_ver": "Z39.88-2004",
    "rft_val_fmt": "info:ofi/fmt:kev:mtx:journal",
    "rfr_id": "info:sid/arxiv-sanity.com:arxiv-sanity",

    "rft_id": p.link,
    "rft.atitle": p.title,
    "rft.jtitle": "arXiv:" + p.pid + " [" + p.category.substring(0, p.category.indexOf('.')) + "]",
    "rft.date": p.published_time,
    "rft.artnum": p.pid,
    "rft.genre": "preprint",

    // NB: Stolen from Dublin Core; Zotero understands this even though it's
    // not part of COinS
    "rft.description": p.abstract,
  };
  ocoins_info = $.param(ocoins_info);
  ocoins_info += "&" + $.map(p.authors, function(a) {
      return "rft.au=" + encodeURIComponent(a);
    }).join("&");

  return ocoins_info;
}

function build_authors_html(authors) {
	var res = '';
	for(var i=0,n=authors.length;i<n;i++) {
		var link = '/search?q=' + authors[i].replace(/ /g, "+");
		res += '<a href="' + link + '">' + authors[i] + '</a>';
		if(i<n-1) res += ', ';
	}
	return res;
}

function build_categories_html(tags) {
    var res = '';
    for(var i=0,n=tags.length;i<n;i++) {
        var link = '/search?q=' + tags[i].replace(/ /g, "+");
        res += '<a href="' + link + '">' + tags[i] + '</a>';
		if(i<n-1) res += ' | ';
    }
    return res;
}

// here we populate papers into #rtable
var pointer_ix = 0; // points to next paper in line to be added to #rtable
var showed_end_msg = false;
function addPapers(num, dynamic) {
  if(papers.length === 0) { return; } // nothing to display

  var root = d3.select("#rtable");

  var base_ix = pointer_ix;
  for(var i=0;i<num;i++) {
    var ix = base_ix + i;
    if(ix >= papers.length) {
      if(!showed_end_msg) {
        if (ix >= {{numresults}}){
          var msg = 'Results complete.';
        } else {
          var msg = 'You hit the limit of number of papers to show in one result.';
        }
        root.append('div').classed('msg', true).html(msg);
        showed_end_msg = true;
      }
      break;
    }
    pointer_ix++;

  	var p = papers[ix];
  	var div = root.append('div').classed('apaper', true).attr('id', p.pid);

    // Generate OpenURL COinS metadata element -- readable by Zotero, Mendeley, etc.
    var ocoins_span = div.append('span').classed('Z3988', true).attr('title', build_ocoins_str(p));

  	var tdiv = div.append('div').classed('paperdesc', true);
  	tdiv.append('span').classed('ts', true).append('a').attr('href', p.link).attr('target', '_blank').html(p.title);
  	tdiv.append('br');
  	tdiv.append('span').classed('as', true).html(build_authors_html(p.authors));
  	tdiv.append('br');
  	tdiv.append('span').classed('ds', true).html(p.published_time);
    if(p.originally_published_time !== p.published_time) {
      tdiv.append('span').classed('ds2', true).html('(v1: ' + p.originally_published_time + ')');
    }
  	tdiv.append('span').classed('cs', true).html(build_categories_html(p.tags));
  	tdiv.append('br');
  	tdiv.append('span').classed('ccs', true).html(p.comment);

    // action items for each paper
  	var ldiv = div.append('div').classed('dllinks', true);
    // show raw arxiv id
  	ldiv.append('span').classed('spid', true).html(p.pid);
    // access PDF of the paper
    var pdf_link = p.link.replace("abs", "pdf"); // convert from /abs/ link to /pdf/ link. url hacking. slightly naughty
    if(pdf_link === p.link) { var pdf_url = pdf_link } // replace failed, lets fall back on arxiv landing page
    else { var pdf_url = pdf_link + '.pdf'; }
  	ldiv.append('a').attr('href', pdf_url).attr('target', '_blank').html('pdf');
    
    // rank by tfidf similarity
    ldiv.append('br');
    var similar_span = ldiv.append('span').classed('sim', true).attr('id', 'sim'+p.pid).html('show similar');
    similar_span.on('click', function(pid){ // attach a click handler to redirect for similarity search
      return function() { window.location.replace('/' + pid); }
    }(p.pid)); // closer over the paper id

    var review_span = ldiv.append('span').classed('sim', true).attr('style', 'margin-left:5px; padding-left: 5px; border-left: 1px solid black;').append('a').attr('href', 'http://www.shortscience.org/paper?bibtexKey='+p.pid).html('review');
    ldiv.append('br');

    var lib_state_img = p.in_library === 1 ? 'static/saved.png' : 'static/save.png';
    var saveimg = ldiv.append('img').attr('src', lib_state_img)
                    .classed('save-icon', true)
                    .attr('title', 'toggle save paper to library (requires login)')
                    .attr('id', 'lib'+p.pid);
    // attach a handler for in-library toggle
    saveimg.on('click', function(pid, elt){
      return function() {
        if(username !== '') {
          // issue the post request to the server
          $.post("/libtoggle", {pid: pid})
           .done(function(data){
              // toggle state of the image to reflect the state of the server, as reported by response
              if(data === 'ON') {
                elt.attr('src', 'static/saved.png');
              } else if(data === 'OFF') {
                elt.attr('src', 'static/save.png');
              }
           });
        } else {
          alert('you must be logged in to save papers to library.')
        }
      }
    }(p.pid, saveimg)); // close over the pid and handle to the image

  	div.append('div').attr('style', 'clear:both');

  	if(typeof p.img !== 'undefined') {
  		div.append('div').classed('animg', true).append('img').attr('src', p.img);
  	}

  	if(typeof p.abstract !== 'undefined') {
  		var abdiv = div.append('span').classed('tt', true).html(p.abstract);
      if(dynamic) {
        MathJax.Hub.Queue(["Typeset",MathJax.Hub,abdiv[0]]); //typeset the added paper
      }
  	}

    if(render_format == 'paper' && ix === 0) {
      // lets insert a divider/message
      div.append('div').classed('paperdivider', true).html('Most similar papers:');
    }
  }
}

// when page loads...
$(document).ready(function(){
	
	urlq = QueryString.q;

  // display message, if any
  if(msg !== '') { d3.select("#rtable").append('div').classed('msg', true).html(msg); }

  // add papers to #rtable
	addPapers(20, false);

  // set up inifinite scrolling for adding more papers
  $(window).on('scroll', function(){
    var scrollTop = $(document).scrollTop();
    var windowHeight = $(window).height();
    var bodyHeight = $(document).height() - windowHeight;
    var scrollPercentage = (scrollTop / bodyHeight);
    if(scrollPercentage > 0.9) {
      addPapers(5, true);
    }
  });

	if(!(typeof urlq == 'undefined')) {
		d3.select("#qfield").attr('value', urlq.replace(/\+/g, " "));
	}

  var vf = QueryString.vfilter; if(typeof vf === 'undefined') { vf = 'all'; }
  var tf = QueryString.timefilter; if(typeof tf === 'undefined') { tf = 'week'; }
  var link_endpoint = '/';
  if(render_format === 'recent') { link_endpoint = ''; }
  if(render_format === 'top') { link_endpoint = 'top'; }
  if(render_format === 'recommend') { link_endpoint = 'recommend'; }

  var time_ranges = ['day', '3days', 'week', 'month', 'year', 'alltime'];
  var time_txt = {'day':'Last day', '3days': 'Last 3 days', 'week': 'Last week', 'month': 'Last month', 'year': 'Last year', 'alltime': 'All time'}
  var time_range = tf;

  // set up time filtering options
  if(render_format === 'recommend' || render_format === 'top' || render_format === 'recent') {
    // insert version filtering options for these views
    var elt = d3.select('#recommend-time-choice');
    var vflink = vf === 'all' ? '1' : 'all'; // toggle only showing v1 or not
    if(render_format === 'recent') {
      var aelt = elt.append('a').attr('href', '/'+link_endpoint+'?'+'&vfilter='+vflink); // leave out timefilter from this page
    } else {
      var aelt = elt.append('a').attr('href', '/'+link_endpoint+'?'+'timefilter='+time_range+'&vfilter='+vflink);
    }
    var delt = aelt.append('div').classed('vchoice', true).html('Only show v1');
    if(vf === '1') { delt.classed('vchoice-selected', true); }
  }

  if(render_format === 'recommend' || render_format === 'top') {
    // insert time filtering options for these two views
    var elt = d3.select('#recommend-time-choice');
    elt.append('div').classed('fdivider', true).html('|');
    for(var i=0;i<time_ranges.length;i++) {
      var time_range = time_ranges[i];
      var aelt = elt.append('a').attr('href', '/'+link_endpoint+'?'+'timefilter='+time_range+'&vfilter='+vf);
      var delt = aelt.append('div').classed('timechoice', true).html(time_txt[time_range]);
      if(tf == time_range) { delt.classed('timechoice-selected', true); } // also render as chosen
    }
  }

  var xb = $("#xbanner");
  if(xb.length !== 0) {
    xb.click(function(){ $("#banner").slideUp('fast'); })
  }

  // in top tab: color current choice
  if( render_format === 'recent') { d3.select('#tabrecent').classed('tab-selected', true); }
  if( render_format === 'top') { d3.select('#tabtop').classed('tab-selected', true); }
  if( render_format === 'recommend') { d3.select('#tabrec').classed('tab-selected', true); }
  if( render_format === 'library') { d3.select('#tablib').classed('tab-selected', true); }
});

</script>
</head>

<body>
<a href="https://github.com/karpathy/arxiv-sanity-preserver"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/38ef81f8aca64bb9a64448d0d70f1308ef5341ab/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6461726b626c75655f3132313632312e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"></a>

<div id ="titdiv">

  <!-- User account information on top right -->
  <div id="userinfo">
    {% if not g.user %}
    <form action="{{ url_for('login') }}" method="post">
      User:
      <input type="text" name="username" class="input-no-border">
      Pass:
      <input type="password" name="password" class="input-no-border">
      <input type="submit" value="Login or Create" class="btn-fancy">
    </form>
    {% else %}
    {{ g.user.username }}
    <a href="{{ url_for('logout') }}">log out</a>
    {% endif %}
  </div>

  <!-- Site information/banner on top left -->
	<a href="/">
	<div id="tittxt">
		<h1>Arxiv Sanity Preserver</h1>
		Built by <a href="https://twitter.com/karpathy">@karpathy</a> to accelerate research.<br>
		Serving last {{ totpapers }} papers from cs.[CV|CL|LG|AI|NE]/stat.ML
	</div>
	</a>
</div>

<div id="flashesdiv">
{% with flashes = get_flashed_messages() %}
    {% if flashes %}
      <ul class="flashes">
      {% for message in flashes %}
        <li>{{ message }}
      {% endfor %}
      </ul>
    {% endif %}
{% endwith %}
</div>

{% if render_format == "recent" and g.user.username is not defined %}
<div id="banner">
  <div style="float:right;cursor:pointer;" id="xbanner">X</div>
  New to arxiv-sanity? Check out the <a href="https://youtu.be/S2GY3gh6qC8" target="_blank">introduction video</a>.
</div>
{% endif %}

<div id="sbox">
  <form action="/search" method="get">
  	<input name="q" type="text" id="qfield">
  </form>
  <div id="search_hint"></div>
</div>

<div id="pagebar">
  <div class="pagelink" id="tabrecent"><a href="/">most recent</a></div>
  <div class="pagelink" id="tabtop"><a href="/top">top recent</a></div>
  <div class="pagelink" id="tabrec"><a href="/recommend">recommended</a></div>
  <div class="pagelink" id="tablib"><a href="/library">library</a></div>
</div>

<!-- this div will be rendered into dynamcially at init with JS -->
<div id="recommend-time-choice" class="centerdiv"></div>

<div id="maindiv">

<div id="rtable">
</div>

</div>

<br><br><br><br><br><br>
</body>

</html>
